package fyp.spedmew.backend.entity;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiNamespace;
import com.google.api.server.spi.config.Named;
import com.google.api.server.spi.config.Nullable;
import com.google.api.server.spi.response.CollectionResponse;
import com.google.api.server.spi.response.ConflictException;
import com.google.api.server.spi.response.NotFoundException;
import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.api.datastore.QueryResultIterator;
import com.googlecode.objectify.cmd.Query;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import static fyp.spedmew.backend.service.OfyService.ofy;

@Api(name = "userEndpoint", version = "v1", namespace = @ApiNamespace(ownerDomain = "entity.backend.spedmew.fyp", ownerName = "entity.backend.spedmew.fyp", packagePath=""))
public class UserEndpoint {
    private static final Logger log = Logger.getLogger(UserEndpoint.class.getName());

    // Make sure to add this endpoint to your web.xml file if this is a web application.
    public UserEndpoint() {

    }

    /**
     * Return a collection of Users
     *
     * @param count The number of Users
     * @return a list of Users
     */
    public CollectionResponse<User> listUser(@Nullable @Named("cursor") String cursorString,
                                               @Nullable @Named("count") Integer count) {
        log.info("listUser called");
        Query<User> query = ofy().load().type(User.class);
        if (count != null) query.limit(count);
        if (cursorString != null && cursorString != "") {
            query = query.startAt(Cursor.fromWebSafeString(cursorString));
        }

        List<User> records = new ArrayList<>();
        QueryResultIterator<User> iterator = query.iterator();
        int num = 0;
        while (iterator.hasNext()) {
            records.add(iterator.next());
            if (count != null) {
                num++;
                if (num == count) break;
            }
        }

        //Find the next cursor
        if (cursorString != null && cursorString != "") {
            Cursor cursor = iterator.getCursor();
            if (cursor != null) {
                cursorString = cursor.toWebSafeString();
            }
        }
        return CollectionResponse.<User>builder().setItems(records).setNextPageToken(cursorString).build();
    }

    /**
     * This inserts a new <code>User</code> object.
     * @param user The object to be added.
     * @return The object to be added.
     */
    public User insertUser(User user) throws ConflictException {
        log.info("insertUser called");
        //If if is not null, then check if it exists. If yes, throw an Exception
        //that it is already present
        if (user.getId() != null) {
            if (findRecord(user.getId()) != null) {
                throw new ConflictException("Object already exists");
            }
        }
        //Since our @Id field is a Long, Objectify will generate a unique value for us
        //when we use put
        ofy().save().entity(user).now();
        return user;
    }

    /**
     * This updates an existing <code>User</code> object.
     * @param user The object to be added.
     * @return The object to be updated.
     */
    public User updateUser(User user)throws NotFoundException {
        if (findRecord(user.getId()) == null) {
            throw new NotFoundException("User record does not exist");
        }
        ofy().save().entity(user).now();
        return user;
    }

    /**
     * This deletes an existing <code>User</code> object.
     * @param id The id of the object to be deleted.
     */
    public void removeUser(@Named("id") Long id) throws NotFoundException {
        User record = findRecord(id);
        if(record == null) {
            throw new NotFoundException("User Record does not exist");
        }
        ofy().delete().entity(record).now();
    }

    /**
     * This method to retrieve a <code>User</code> record by input username
     * @param username The id of the object to be find.
     */
    public User findByUsername(@Named("username") String username) throws NotFoundException {
        username = username != null ? username.toLowerCase() : null;
        log.info("Find user with name " + username);
        User result = ofy().load().type(User.class).filter("name", username).first().now();
        return result;
    }

    /**
     * This method to retrieve a <code>User</code> record by input phone
     * @param phone The number of the object to be find.
     */
    public User findByPhone(@Named("phone") String phone) throws NotFoundException {
        log.info("Find user with phone number " + phone);
        User result = ofy().load().type(User.class).filter("phone", phone).first().now();
        return result;
    }

    /**
     * This method to retrieve a <code>User</code> record by input id
     * @param id The id of the object to be return.
     */
    public User findRecord(@Named("id") Long id) {
        return ofy().load().type(User.class).id(id).now();
    }
}